Course Setup

install.packages("tidyverse")
library(tidyverse)
-- Attaching packages --------------------------------------- tidyverse 1.2.1 --
v ggplot2 3.0.0     v purrr   0.2.5
v tibble  1.4.2     v dplyr   0.7.6
v tidyr   0.8.1     v stringr 1.3.1
v readr   1.1.1     v forcats 0.3.0
package 㤼㸱ggplot2㤼㸲 was built under R version 3.5.1package 㤼㸱dplyr㤼㸲 was built under R version 3.5.1-- Conflicts ------------------------------------------ tidyverse_conflicts() --
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()

Explore Data

First ggplot

ggplot(data = midwest) +
  geom_point(mapping = aes(x = popdensity, y = percollege))

Equivalent Code

ggplot(midwest) +
  geom_point(aes(x = popdensity, y = percollege))

Your Turn

  1. Try plotting popdensity by state.
  2. Try plotting county by state.
    • Does this plot work?
  3. Bonus: Try just using the ggplot(data = midwest) from above.
    • What do you get?
    • Does this make sense?

Add Aesthetics

ggplot(midwest) +
  geom_point(aes(x = popdensity, y = percollege, color = state))

Global Aesthetics

ggplot(midwest) +
  geom_point(aes(x = popdensity, y = percollege), color = 'pink')

Your Turn

  1. Instead of using colors, make the shape of the points different for each state.
  2. Instead of color, use alpha instead.
    • What does this do to the plot?
  3. Try the following command: colors().
    • Try a few colors to find your favorite.
  4. What happens if you use the following code:
ggplot(midwest) + 
  geom_point(aes(x = popdensity, y = percollege, color = 'green'))

Additional Geoms

ggplot(midwest) +
  geom_smooth(aes(x = popdensity, y = percollege))

Add more Aesthetics

ggplot(midwest) +
  geom_smooth(aes(x = popdensity, y = percollege, linetype = state), 
              se = FALSE)

Your Turn

  1. It is possible to combine geoms, which we will do next, but try it first. Try to recreate this plot.

Layered ggplot

ggplot(midwest) +
  geom_point(aes(x = popdensity, y = percollege, color = state)) +
  geom_smooth(aes(x = popdensity, y = percollege, color = state), 
              se = FALSE)

Remove duplicate aesthetics

ggplot(midwest, 
       aes(x = popdensity, y = percollege, color = state)) +
  geom_point() +
  geom_smooth(se = FALSE)

Your Turn

  1. Can you recreate the following figure?

Brief plot customization

ggplot(midwest, 
       aes(x = popdensity, y = percollege, color = state)) +
  geom_point() + 
  scale_x_continuous("Population Density", 
                     breaks = seq(0, 80000, 20000)) + 
  scale_y_continuous("Percent College Graduates") + 
  scale_color_discrete("State")

Brief plot customization Output

Additional ggplot2 resources

R works as a calculator

1 + 2 - 3
[1] 0
5 * 7
[1] 35
2/1
[1] 2

R Calculator 2

sqrt(4)
[1] 2
2^2
[1] 4

Can save objects to use later

x <- 1 + 3
x
[1] 4
x * 3
[1] 12

R is case sensitive

case_sensitive <- 10
Case_sensitive
Error: object 'Case_sensitive' not found

R Functions

set.seed(1)
rnorm(n = 5, mean = 0, sd = 1)
[1] -0.6264538  0.1836433 -0.8356286  1.5952808  0.3295078
set.seed(1)
rnorm(5, 0, 1)
[1] -0.6264538  0.1836433 -0.8356286  1.5952808  0.3295078
set.seed(1)
rnorm(sd = 1, n = 5, mean = 0)
[1] -0.6264538  0.1836433 -0.8356286  1.5952808  0.3295078

Working through Errors

  1. Use ?function_name to explore the details of the function. The examples at the bottom of every R help page can be especially helpful.
  2. If this does not help, copy and paste the error and search on the internet.

Using dplyr for data manipulation

The dplyr package uses verbs for common data manipulation tasks. These include:

Data

https://fivethirtyeight.com/features/both-republicans-and-democrats-have-an-age-problem/

install.packages('fivethirtyeight')
library(fivethirtyeight)

Using filter

filter(congress_age, congress == 80)

Save filtered results to object

congress_80 <- filter(congress_age, congress == 80)

Other operators for numbers

Your Turn

  1. Select all rows where the congress member was older than 80 at the start of the term.
  2. Use the is.na function to identify congress members that have missing middlenames.

Filter character variables

filter(congress_age, chamber == 'senate')

Combine Operations - AND

filter(congress_age, congress == 80, chamber == 'senate')

Equivalent AND Statement

filter(congress_age, congress == 80 & chamber == 'senate')

Filter - OR

filter(congress_age, congress == 80 | congress == 81)

%in%

filter(congress_age, congress %in% c(80, 81))

Not Operator

filter(congress_age, congress != 80)

Not Operator 2

filter(congress_age, congress == 80 & !chamber == 'senate')

Your Turn

  1. Select the Senators from Iowa.
  2. Select the Senators from Iowa that are not inbumbents.

All boolean options

Using count

count(congress_age, party, incumbent)

Using arrange

arrange(congress_age, state, party)

Descending Order

arrange(congress_age, desc(congress))

Your Turn

  1. Count the number of congress members from each party that are older than 80 at term start.
  2. Arrange the result from above by the variable n.

Using select

select(congress_age, congress, chamber, party, age)

Helper functions

starts_with helper

select(congress_age, starts_with('s'))

Contains helper

select(congress_age, contains('name'))

Colon

select(congress_age, congress:birthday)

Drop variables

select(congress_age, -firstname, -state, -party, -incumbent, -chamber)

Reorder with everything

select(congress_age, congress, chamber, incumbent, age, everything())

rename function

rename(congress_age, first_name = firstname, last_name = lastname)

Your Turn

  1. Using the dplyr helper functions, select all the variables that start with the letter ‘c’.
  2. Rename the first three variables in the congress data to ‘x1’, ‘x2’, ‘x3’.
  3. After renaming the first three variables, use this new data (ensure you saved the previous step to an object) to select these three variables with the num_range function.

Using mutate

congress_red <- select(congress_age, congress, chamber, state, party)
mutate(congress_red, 
       democrat = ifelse(party == 'D', 1, 0),
       num_democrat = sum(democrat)
       )

Your Turn

  1. Using the diamonds data, use ?diamonds for more information on the data, use the mutate function to calculate the price per carat. Hint, this operation would involve standardizing the price variable so that all are comparable at 1 carat.
  2. Using mutate, calculate the rank of the original price variable and the new price variable calculated above using the min_rank function. Are there differences in the ranking of the prices?

Using summarise

congress_2 <- mutate(congress_age, 
       democrat = ifelse(party == 'D', 1, 0)
       )
summarise(congress_2, 
          num_democrat = sum(democrat)
          )

group_by

congress_grp <- group_by(congress_2, congress)
summarise(congress_grp, 
          num_democrat = sum(democrat),
          total = n(),
          prop_democrat = num_democrat / total
)

Explore trend

num_dem <- summarise(congress_grp, 
                     num_democrat = sum(democrat),
                     total = n(),
                     prop_democrat = num_democrat / total
)
ggplot(num_dem, aes(x = congress, y = prop_democrat)) + 
  geom_line()

Explore trend output

Your Turn

  1. Suppose we wanted to calculate the number and proportion of republicans instead of democrats, assuming these are the only two parties, edit the summarise command above to calculate these values.
  2. Suppose instead of using sum(democrat) above, we used mean(democrat), what does this value return? Why does it return this value?

group_by with mutate

congress_red <- select(congress_age, congress, chamber, state, party)
congress_grp <- group_by(congress_red, congress)

mutate(congress_grp, 
       democrat = ifelse(party == 'D', 1, 0),
       num_democrat = sum(democrat),
       total = n(),
       prop_democrat = num_democrat / total
)

group_by with mutate output

Chaining operations

summarise(
  group_by(
    mutate(
      filter(
        congress_age, congress >= 100
      ), 
      democrat = ifelse(party == 'D', 1, 0)
    ),
    congress, chamber
  ),
  num_democrat = sum(democrat),
  total = n(),
  prop_democrat = num_democrat / total
)

The pipe %>% is the answer

congress_age %>%
  filter(congress >= 100) %>%
  mutate(democrat = ifelse(party == 'D', 1, 0)) %>%
  group_by(congress, chamber) %>%
  summarise(
    num_democrat = sum(democrat),
    total = n(),
    prop_democrat = num_democrat / total
  )

The two are identical

pipe_congress <- congress_age %>%
  filter(congress >= 100) %>%
  mutate(democrat = ifelse(party == 'D', 1, 0)) %>%
  group_by(congress, chamber) %>%
  summarise(
    num_democrat = sum(democrat),
    total = n(),
    prop_democrat = num_democrat / total
  )
nested_congress <- summarise(
  group_by(
    mutate(
      filter(
        congress_age, congress >= 100
      ), 
      democrat = ifelse(party == 'D', 1, 0)
    ),
    congress, chamber
  ),
  num_democrat = sum(democrat),
  total = n(),
  prop_democrat = num_democrat / total
)
identical(pipe_congress, nested_congress)
[1] TRUE

Your Turn

  1. Look at the following nested code and determine what is being done. Then translate this code to use the pipe operator.
summarise(
  group_by(
    mutate(
      filter(
        diamonds, 
        color %in% c('D', 'E', 'F') & cut %in% c('Fair', 'Good', 'Very Good')
      ),
      f_color = ifelse(color == 'F', 1, 0),
      vg_cut = ifelse(cut == 'Very Good', 1, 0)
    ),
    clarity
  ),
  avg = mean(carat),
  sd = sd(carat),
  avg_p = mean(price),
  num = n(),
  summary_f_color = mean(f_color),
  summary_vg_cut = mean(vg_cut)
)

Read in your own data

Data Import

ufo <- read_csv("https://raw.githubusercontent.com/lebebr01/iowa_data_science/master/data/ufo.csv")
Parsed with column specification:
cols(
  `Date / Time` = col_character(),
  City = col_character(),
  State = col_character(),
  Shape = col_character(),
  Duration = col_character(),
  Summary = col_character(),
  Posted = col_character()
)

Show Data

ufo

Other text formats

Your Turn

  1. There is a tsv file posted on icon called “lotr_clean.tsv”. Download this and read this data file into R.
  2. Instead of specifying the path, use the function file.choose(). For example, read_tsv(file.choose()).
    • What does this function do?
    • Would you recommend this to be used in a reproducible document?

Excel Files

install.packages('readxl')

read_excel

library(readxl)
read_excel('data/titanic.xlsx')

Additional Resources

LS0tDQp0aXRsZTogIkludHJvZHVjdGlvbiB0byB0aWR5dmVyc2UvUiINCmF1dGhvcjogIkJyYW5kb24gTGVCZWF1Ig0KZGF0ZTogIkphbnVhcnkgMTAsIDIwMTgiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIENvdXJzZSBTZXR1cA0KYGBge3Igc2V0dXBfY2h1bmtzLCBlY2hvID0gRkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTYsIGZpZy5jYXAgPSBOVUxMKSANCmBgYA0KDQpgYGB7ciBzZXR1cCwgbWVzc2FnZSA9IEZBTFNFLCBldmFsID0gRkFMU0V9DQppbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQ0KYGBgDQoNCmBgYHtyIGxpYnJhcnl9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmBgYA0KDQojIEV4cGxvcmUgRGF0YQ0KYGBge3IgZGF0YSwgZWNobyA9IEZBTFNFLCByZXN1bHRzID0gJ2FzaXMnfQ0KbWlkd2VzdA0KYGBgDQoNCiMgRmlyc3QgZ2dwbG90DQpgYGB7ciBwbG90MX0NCmdncGxvdChkYXRhID0gbWlkd2VzdCkgKw0KICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IHBvcGRlbnNpdHksIHkgPSBwZXJjb2xsZWdlKSkNCmBgYA0KDQojIEVxdWl2YWxlbnQgQ29kZQ0KYGBge3IgcGxvdDFfcmVkdWNlZH0NCmdncGxvdChtaWR3ZXN0KSArDQogIGdlb21fcG9pbnQoYWVzKHggPSBwb3BkZW5zaXR5LCB5ID0gcGVyY29sbGVnZSkpDQpgYGANCg0KIyBZb3VyIFR1cm4NCjEuIFRyeSBwbG90dGluZyBgcG9wZGVuc2l0eWAgYnkgYHN0YXRlYC4NCjIuIFRyeSBwbG90dGluZyBgY291bnR5YCBieSBgc3RhdGVgLiANCiAgICArIERvZXMgdGhpcyBwbG90IHdvcms/DQozLiBCb251czogVHJ5IGp1c3QgdXNpbmcgdGhlIGBnZ3Bsb3QoZGF0YSA9IG1pZHdlc3QpYCBmcm9tIGFib3ZlLiANCiAgICArIFdoYXQgZG8geW91IGdldD8gDQogICAgKyBEb2VzIHRoaXMgbWFrZSBzZW5zZT8NCg0KIyBBZGQgQWVzdGhldGljcw0KYGBge3IgYWVzdGhldGljfQ0KZ2dwbG90KG1pZHdlc3QpICsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IHBvcGRlbnNpdHksIHkgPSBwZXJjb2xsZWdlLCBjb2xvciA9IHN0YXRlKSkNCmBgYA0KDQojIEdsb2JhbCBBZXN0aGV0aWNzDQpgYGB7ciBnbG9iYWxfYWVzfQ0KZ2dwbG90KG1pZHdlc3QpICsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IHBvcGRlbnNpdHksIHkgPSBwZXJjb2xsZWdlKSwgY29sb3IgPSAncGluaycpDQpgYGANCg0KIyBZb3VyIFR1cm4NCjEuIEluc3RlYWQgb2YgdXNpbmcgY29sb3JzLCBtYWtlIHRoZSBzaGFwZSBvZiB0aGUgcG9pbnRzIGRpZmZlcmVudCBmb3IgZWFjaCBzdGF0ZS4NCjIuIEluc3RlYWQgb2YgY29sb3IsIHVzZSBgYWxwaGFgIGluc3RlYWQuIA0KICAgICsgV2hhdCBkb2VzIHRoaXMgZG8gdG8gdGhlIHBsb3Q/DQozLiBUcnkgdGhlIGZvbGxvd2luZyBjb21tYW5kOiBgY29sb3JzKClgLiANCiAgICArIFRyeSBhIGZldyBjb2xvcnMgdG8gZmluZCB5b3VyIGZhdm9yaXRlLg0KNC4gV2hhdCBoYXBwZW5zIGlmIHlvdSB1c2UgdGhlIGZvbGxvd2luZyBjb2RlOg0KYGBge3IgZ2xvYl9hZXMsIGZpZy5zaG93ID0gJ2hpZGUnfQ0KZ2dwbG90KG1pZHdlc3QpICsgDQogIGdlb21fcG9pbnQoYWVzKHggPSBwb3BkZW5zaXR5LCB5ID0gcGVyY29sbGVnZSwgY29sb3IgPSAnZ3JlZW4nKSkNCmBgYA0KDQojIEFkZGl0aW9uYWwgR2VvbXMNCmBgYHtyIHNtb290aCwgbWVzc2FnZSA9IEZBTFNFfQ0KZ2dwbG90KG1pZHdlc3QpICsNCiAgZ2VvbV9zbW9vdGgoYWVzKHggPSBwb3BkZW5zaXR5LCB5ID0gcGVyY29sbGVnZSkpDQpgYGANCg0KIyBBZGQgbW9yZSBBZXN0aGV0aWNzDQpgYGB7ciBzbW9vdGhfc3RhdGVzLCBtZXNzYWdlID0gRkFMU0V9DQpnZ3Bsb3QobWlkd2VzdCkgKw0KICBnZW9tX3Ntb290aChhZXMoeCA9IHBvcGRlbnNpdHksIHkgPSBwZXJjb2xsZWdlLCBsaW5ldHlwZSA9IHN0YXRlKSwgDQogICAgICAgICAgICAgIHNlID0gRkFMU0UpDQpgYGANCg0KIyBZb3VyIFR1cm4NCjEuIEl0IGlzIHBvc3NpYmxlIHRvIGNvbWJpbmUgZ2VvbXMsIHdoaWNoIHdlIHdpbGwgZG8gbmV4dCwgYnV0IHRyeSBpdCBmaXJzdC4gVHJ5IHRvIHJlY3JlYXRlIHRoaXMgcGxvdC4NCmBgYHtyIGNvbWJpbmUsIGVjaG8gPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQ0KZ2dwbG90KG1pZHdlc3QpICsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IHBvcGRlbnNpdHksIHkgPSBwZXJjb2xsZWdlLCBjb2xvciA9IHN0YXRlKSkgKw0KICBnZW9tX3Ntb290aChhZXMoeCA9IHBvcGRlbnNpdHksIHkgPSBwZXJjb2xsZWdlLCBjb2xvciA9IHN0YXRlKSwgDQogICAgICAgICAgICAgIHNlID0gRkFMU0UpDQpgYGANCg0KIyBMYXllcmVkIGdncGxvdA0KYGBge3IgY29tYmluZV9nZW9tcywgbWVzc2FnZSA9IEZBTFNFfQ0KZ2dwbG90KG1pZHdlc3QpICsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IHBvcGRlbnNpdHksIHkgPSBwZXJjb2xsZWdlLCBjb2xvciA9IHN0YXRlKSkgKw0KICBnZW9tX3Ntb290aChhZXMoeCA9IHBvcGRlbnNpdHksIHkgPSBwZXJjb2xsZWdlLCBjb2xvciA9IHN0YXRlKSwgDQogICAgICAgICAgICAgIHNlID0gRkFMU0UpDQpgYGANCg0KIyBSZW1vdmUgZHVwbGljYXRlIGFlc3RoZXRpY3MNCmBgYHtyIHR3b19nZW9tcywgbWVzc2FnZSA9IEZBTFNFfQ0KZ2dwbG90KG1pZHdlc3QsIA0KICAgICAgIGFlcyh4ID0gcG9wZGVuc2l0eSwgeSA9IHBlcmNvbGxlZ2UsIGNvbG9yID0gc3RhdGUpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21fc21vb3RoKHNlID0gRkFMU0UpDQpgYGANCg0KIyBZb3VyIFR1cm4NCjEuIENhbiB5b3UgcmVjcmVhdGUgdGhlIGZvbGxvd2luZyBmaWd1cmU/DQpgYGB7ciBkaWZmZXJfYWVzLCBtZXNzYWdlID0gRkFMU0UsIGVjaG8gPSBGQUxTRX0NCmdncGxvdChtaWR3ZXN0LCBhZXMoeCA9IHBvcGRlbnNpdHksIHkgPSBwZXJjb2xsZWdlKSkgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IHN0YXRlKSkgKw0KICBnZW9tX3Ntb290aChzZSA9IEZBTFNFKQ0KYGBgDQoNCiMgQnJpZWYgcGxvdCBjdXN0b21pemF0aW9uDQpgYGB7ciBicmVha3NfeCwgZXZhbCA9IEZBTFNFfQ0KZ2dwbG90KG1pZHdlc3QsIA0KICAgICAgIGFlcyh4ID0gcG9wZGVuc2l0eSwgeSA9IHBlcmNvbGxlZ2UsIGNvbG9yID0gc3RhdGUpKSArDQogIGdlb21fcG9pbnQoKSArIA0KICBzY2FsZV94X2NvbnRpbnVvdXMoIlBvcHVsYXRpb24gRGVuc2l0eSIsIA0KICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKDAsIDgwMDAwLCAyMDAwMCkpICsgDQogIHNjYWxlX3lfY29udGludW91cygiUGVyY2VudCBDb2xsZWdlIEdyYWR1YXRlcyIpICsgDQogIHNjYWxlX2NvbG9yX2Rpc2NyZXRlKCJTdGF0ZSIpDQpgYGANCg0KIyBCcmllZiBwbG90IGN1c3RvbWl6YXRpb24gT3V0cHV0DQpgYGB7ciBicmVha3NfeDIsIGVjaG8gPSBGQUxTRX0NCmdncGxvdChtaWR3ZXN0LCANCiAgICAgICBhZXMoeCA9IHBvcGRlbnNpdHksIHkgPSBwZXJjb2xsZWdlLCBjb2xvciA9IHN0YXRlKSkgKw0KICBnZW9tX3BvaW50KCkgKyANCiAgc2NhbGVfeF9jb250aW51b3VzKCJQb3B1bGF0aW9uIERlbnNpdHkiLCANCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSgwLCA4MDAwMCwgMjAwMDApKSArIA0KICBzY2FsZV95X2NvbnRpbnVvdXMoIlBlcmNlbnQgQ29sbGVnZSBHcmFkdWF0ZXMiKSArIA0KICBzY2FsZV9jb2xvcl9kaXNjcmV0ZSgiU3RhdGUiKQ0KYGBgDQoNCg0KIyBBZGRpdGlvbmFsIGdncGxvdDIgcmVzb3VyY2VzDQorIGdncGxvdDIgd2Vic2l0ZTogPGh0dHA6Ly9kb2NzLmdncGxvdDIub3JnL2N1cnJlbnQvaW5kZXguaHRtbD4NCisgZ2dwbG90MiBib29rOiA8aHR0cDovL3d3dy5zcHJpbmdlci5jb20vdXMvYm9vay85NzgwMzg3OTgxNDEzPg0KKyBSIGdyYXBoaWNzIGNvb2tib29rOiA8aHR0cDovL3d3dy5jb29rYm9vay1yLmNvbS9HcmFwaHMvPg0KDQoNCiMgUiB3b3JrcyBhcyBhIGNhbGN1bGF0b3INCmBgYHtyIGNhbGN9DQoxICsgMiAtIDMNCjUgKiA3DQoyLzENCmBgYA0KDQojIFIgQ2FsY3VsYXRvciAyDQpgYGB7ciBjYWxjMn0NCnNxcnQoNCkNCjJeMg0KYGBgDQoNCg0KDQojIENhbiBzYXZlIG9iamVjdHMgdG8gdXNlIGxhdGVyDQpgYGB7ciBvYmplY3R9DQp4IDwtIDEgKyAzDQp4DQpgYGANCg0KYGBge3Igb2JqZWN0Mn0NCnggKiAzDQpgYGANCg0KIyBSIGlzIGNhc2Ugc2Vuc2l0aXZlDQpgYGB7ciBjYXNlLCBlcnJvciA9IFRSVUV9DQpjYXNlX3NlbnNpdGl2ZSA8LSAxMA0KQ2FzZV9zZW5zaXRpdmUNCmBgYA0KDQojIFIgRnVuY3Rpb25zDQpgYGB7ciBybm9ybX0NCnNldC5zZWVkKDEpDQpybm9ybShuID0gNSwgbWVhbiA9IDAsIHNkID0gMSkNCmBgYA0KYGBge3Igcm5vcm0yfQ0Kc2V0LnNlZWQoMSkNCnJub3JtKDUsIDAsIDEpDQpgYGANCmBgYHtyIHJub3JtM30NCnNldC5zZWVkKDEpDQpybm9ybShzZCA9IDEsIG4gPSA1LCBtZWFuID0gMCkNCmBgYA0KDQojIFdvcmtpbmcgdGhyb3VnaCBFcnJvcnMNCjEuIFVzZSBgP2Z1bmN0aW9uX25hbWVgIHRvIGV4cGxvcmUgdGhlIGRldGFpbHMgb2YgdGhlIGZ1bmN0aW9uLiBUaGUgZXhhbXBsZXMgYXQgdGhlIGJvdHRvbSBvZiBldmVyeSBSIGhlbHAgcGFnZSBjYW4gYmUgZXNwZWNpYWxseSBoZWxwZnVsLg0KICAgICsgPGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy8+IHByb3ZpZGVkIGJ5IERhdGFDYW1wIGlzIGEgZ3JlYXQgYWx0ZXJuYXRpdmUgYXMgd2VsbC4NCjIuIElmIHRoaXMgZG9lcyBub3QgaGVscCwgY29weSBhbmQgcGFzdGUgdGhlIGVycm9yIGFuZCBzZWFyY2ggb24gdGhlIGludGVybmV0Lg0KDQojIFVzaW5nIGBkcGx5cmAgZm9yIGRhdGEgbWFuaXB1bGF0aW9uDQpUaGUgYGRwbHlyYCBwYWNrYWdlIHVzZXMgdmVyYnMgZm9yIGNvbW1vbiBkYXRhIG1hbmlwdWxhdGlvbiB0YXNrcy4gVGhlc2UgaW5jbHVkZToNCg0KLSBgZmlsdGVyKClgDQotIGBjb3VudCgpYA0KLSBgYXJyYW5nZSgpYA0KLSBgc2VsZWN0KClgDQotIGBtdXRhdGUoKWANCi0gYHN1bW1hcmlzZSgpYA0KDQojIERhdGENCjxodHRwczovL2ZpdmV0aGlydHllaWdodC5jb20vZmVhdHVyZXMvYm90aC1yZXB1YmxpY2Fucy1hbmQtZGVtb2NyYXRzLWhhdmUtYW4tYWdlLXByb2JsZW0vPg0KDQpgYGB7ciBmaXZldGhpcnR5ZWlnaHQsIGV2YWwgPSBGQUxTRX0NCmluc3RhbGwucGFja2FnZXMoJ2ZpdmV0aGlydHllaWdodCcpDQpsaWJyYXJ5KGZpdmV0aGlydHllaWdodCkNCmBgYA0KDQpgYGB7ciBjb25ncmVzcywgZWNobyA9IEZBTFNFfQ0KbGlicmFyeShmaXZldGhpcnR5ZWlnaHQpDQpjb25ncmVzc19hZ2VbMToxMDAsIF0NCmBgYA0KDQoNCiMgVXNpbmcgYGZpbHRlcmANCmBgYHtyIDgwdGh9DQpmaWx0ZXIoY29uZ3Jlc3NfYWdlLCBjb25ncmVzcyA9PSA4MCkNCmBgYA0KDQojIFNhdmUgZmlsdGVyZWQgcmVzdWx0cyB0byBvYmplY3QNCmBgYHtyIDgwdGhfc2F2ZX0NCmNvbmdyZXNzXzgwIDwtIGZpbHRlcihjb25ncmVzc19hZ2UsIGNvbmdyZXNzID09IDgwKQ0KYGBgDQoNCiMgT3RoZXIgb3BlcmF0b3JzIGZvciBudW1iZXJzDQotIGA+YA0KLSBgPGANCi0gYD49YA0KLSBgPD1gDQoNCiMgWW91ciBUdXJuDQoxLiBTZWxlY3QgYWxsIHJvd3Mgd2hlcmUgdGhlIGNvbmdyZXNzIG1lbWJlciB3YXMgb2xkZXIgdGhhbiA4MCBhdCB0aGUgc3RhcnQgb2YgdGhlIHRlcm0uDQoyLiBVc2UgdGhlIGBpcy5uYWAgZnVuY3Rpb24gdG8gaWRlbnRpZnkgY29uZ3Jlc3MgbWVtYmVycyB0aGF0IGhhdmUgbWlzc2luZyBtaWRkbGVuYW1lcy4NCg0KIyBGaWx0ZXIgY2hhcmFjdGVyIHZhcmlhYmxlcw0KYGBge3Igc2VuYXRlfQ0KZmlsdGVyKGNvbmdyZXNzX2FnZSwgY2hhbWJlciA9PSAnc2VuYXRlJykNCmBgYA0KDQojIENvbWJpbmUgT3BlcmF0aW9ucyAtIEFORA0KYGBge3IgODBzZW5hdGV9DQpmaWx0ZXIoY29uZ3Jlc3NfYWdlLCBjb25ncmVzcyA9PSA4MCwgY2hhbWJlciA9PSAnc2VuYXRlJykNCmBgYA0KDQojIEVxdWl2YWxlbnQgQU5EIFN0YXRlbWVudA0KYGBge3IgODBzZW5hdGUyfQ0KZmlsdGVyKGNvbmdyZXNzX2FnZSwgY29uZ3Jlc3MgPT0gODAgJiBjaGFtYmVyID09ICdzZW5hdGUnKQ0KYGBgDQoNCiMgRmlsdGVyIC0gT1INCmBgYHtyIGZpbHRlcl9vcn0NCmZpbHRlcihjb25ncmVzc19hZ2UsIGNvbmdyZXNzID09IDgwIHwgY29uZ3Jlc3MgPT0gODEpDQpgYGANCg0KIyBgJWluJWANCmBgYHtyIGZpbHRlcl8yfQ0KZmlsdGVyKGNvbmdyZXNzX2FnZSwgY29uZ3Jlc3MgJWluJSBjKDgwLCA4MSkpDQpgYGANCg0KIyBOb3QgT3BlcmF0b3INCmBgYHtyIG5vdF84MH0NCmZpbHRlcihjb25ncmVzc19hZ2UsIGNvbmdyZXNzICE9IDgwKQ0KYGBgDQoNCiMgTm90IE9wZXJhdG9yIDINCmBgYHtyIDgwX25vdHNlbmF0ZX0NCmZpbHRlcihjb25ncmVzc19hZ2UsIGNvbmdyZXNzID09IDgwICYgIWNoYW1iZXIgPT0gJ3NlbmF0ZScpDQpgYGANCg0KDQojIFlvdXIgVHVybg0KMS4gU2VsZWN0IHRoZSBTZW5hdG9ycyBmcm9tIElvd2EuDQoyLiBTZWxlY3QgdGhlIFNlbmF0b3JzIGZyb20gSW93YSB0aGF0IGFyZSBub3QgaW5idW1iZW50cy4NCg0KIyBBbGwgYm9vbGVhbiBvcHRpb25zDQohW10oZmlndXJlcy9ib29sZWFuLnBuZykNCg0KIyBVc2luZyBgY291bnRgDQpgYGB7ciBjb3VudH0NCmNvdW50KGNvbmdyZXNzX2FnZSwgcGFydHksIGluY3VtYmVudCkNCmBgYA0KDQojIFVzaW5nIGBhcnJhbmdlYA0KYGBge3Igc2ltcGxlX2FycmFuZ2V9DQphcnJhbmdlKGNvbmdyZXNzX2FnZSwgc3RhdGUsIHBhcnR5KQ0KYGBgDQoNCiMgRGVzY2VuZGluZyBPcmRlcg0KYGBge3IgZGVzY2VuZH0NCmFycmFuZ2UoY29uZ3Jlc3NfYWdlLCBkZXNjKGNvbmdyZXNzKSkNCmBgYA0KDQojIFlvdXIgVHVybg0KMS4gQ291bnQgdGhlIG51bWJlciBvZiBjb25ncmVzcyBtZW1iZXJzIGZyb20gZWFjaCBwYXJ0eSB0aGF0IGFyZSBvbGRlciB0aGFuIDgwIGF0IHRlcm0gc3RhcnQuDQoyLiBBcnJhbmdlIHRoZSByZXN1bHQgZnJvbSBhYm92ZSBieSB0aGUgdmFyaWFibGUgbi4NCg0KIyBVc2luZyBgc2VsZWN0YA0KYGBge3Igc2VsZWN0fQ0Kc2VsZWN0KGNvbmdyZXNzX2FnZSwgY29uZ3Jlc3MsIGNoYW1iZXIsIHBhcnR5LCBhZ2UpDQpgYGANCg0KIyBIZWxwZXIgZnVuY3Rpb25zDQotIGBzdGFydHNfd2l0aCgpYA0KLSBgZW5kc193aXRoKClgDQotIGBjb250YWlucygpYA0KLSBgbWF0Y2hlcygpYA0KLSBgbnVtX3JhbmdlKClgDQotIGA6YA0KLSBgZXZlcnl0aGluZygpYA0KDQojIGBzdGFydHNfd2l0aGAgaGVscGVyDQpgYGB7ciBzdGFydHNfd2l0aH0NCnNlbGVjdChjb25ncmVzc19hZ2UsIHN0YXJ0c193aXRoKCdzJykpDQpgYGANCg0KDQojIENvbnRhaW5zIGhlbHBlcg0KYGBge3IgY29udGFpbnN9DQpzZWxlY3QoY29uZ3Jlc3NfYWdlLCBjb250YWlucygnbmFtZScpKQ0KYGBgDQoNCiMgQ29sb24NCmBgYHtyIGNvbG9ufQ0Kc2VsZWN0KGNvbmdyZXNzX2FnZSwgY29uZ3Jlc3M6YmlydGhkYXkpDQpgYGANCg0KIyBEcm9wIHZhcmlhYmxlcw0KYGBge3IgZHJvcH0NCnNlbGVjdChjb25ncmVzc19hZ2UsIC1maXJzdG5hbWUsIC1zdGF0ZSwgLXBhcnR5LCAtaW5jdW1iZW50LCAtY2hhbWJlcikNCmBgYA0KDQojIFJlb3JkZXIgd2l0aCBgZXZlcnl0aGluZ2ANCmBgYHtyIHJlb3JkZXJ9DQpzZWxlY3QoY29uZ3Jlc3NfYWdlLCBjb25ncmVzcywgY2hhbWJlciwgaW5jdW1iZW50LCBhZ2UsIGV2ZXJ5dGhpbmcoKSkNCmBgYA0KDQojIGByZW5hbWVgIGZ1bmN0aW9uDQpgYGB7ciByZW5hbWV9DQpyZW5hbWUoY29uZ3Jlc3NfYWdlLCBmaXJzdF9uYW1lID0gZmlyc3RuYW1lLCBsYXN0X25hbWUgPSBsYXN0bmFtZSkNCmBgYA0KDQojIFlvdXIgVHVybg0KMS4gVXNpbmcgdGhlIGBkcGx5cmAgaGVscGVyIGZ1bmN0aW9ucywgc2VsZWN0IGFsbCB0aGUgdmFyaWFibGVzIHRoYXQgc3RhcnQgd2l0aCB0aGUgbGV0dGVyICdjJy4NCjIuIFJlbmFtZSB0aGUgZmlyc3QgdGhyZWUgdmFyaWFibGVzIGluIHRoZSBjb25ncmVzcyBkYXRhIHRvICd4MScsICd4MicsICd4MycuDQozLiBBZnRlciByZW5hbWluZyB0aGUgZmlyc3QgdGhyZWUgdmFyaWFibGVzLCB1c2UgdGhpcyBuZXcgZGF0YSAoZW5zdXJlIHlvdSBzYXZlZCB0aGUgcHJldmlvdXMgc3RlcCB0byBhbiBvYmplY3QpIHRvIHNlbGVjdCB0aGVzZSB0aHJlZSB2YXJpYWJsZXMgd2l0aCB0aGUgYG51bV9yYW5nZWAgZnVuY3Rpb24uDQoNCiMgVXNpbmcgYG11dGF0ZWANCmBgYHtyIG11dGF0ZSwgZXJyb3IgPSBGQUxTRX0NCmNvbmdyZXNzX3JlZCA8LSBzZWxlY3QoY29uZ3Jlc3NfYWdlLCBjb25ncmVzcywgY2hhbWJlciwgc3RhdGUsIHBhcnR5KQ0KDQptdXRhdGUoY29uZ3Jlc3NfcmVkLCANCiAgICAgICBkZW1vY3JhdCA9IGlmZWxzZShwYXJ0eSA9PSAnRCcsIDEsIDApLA0KICAgICAgIG51bV9kZW1vY3JhdCA9IHN1bShkZW1vY3JhdCkNCiAgICAgICApDQpgYGANCg0KIyBZb3VyIFR1cm4NCjEuIFVzaW5nIHRoZSBgZGlhbW9uZHNgIGRhdGEsIHVzZSBgP2RpYW1vbmRzYCBmb3IgbW9yZSBpbmZvcm1hdGlvbiBvbiB0aGUgZGF0YSwgdXNlIHRoZSBgbXV0YXRlYCBmdW5jdGlvbiB0byBjYWxjdWxhdGUgdGhlIHByaWNlIHBlciBjYXJhdC4gSGludCwgdGhpcyBvcGVyYXRpb24gd291bGQgaW52b2x2ZSBzdGFuZGFyZGl6aW5nIHRoZSBwcmljZSB2YXJpYWJsZSBzbyB0aGF0IGFsbCBhcmUgY29tcGFyYWJsZSBhdCAxIGNhcmF0Lg0KMi4gVXNpbmcgYG11dGF0ZWAsIGNhbGN1bGF0ZSB0aGUgcmFuayBvZiB0aGUgb3JpZ2luYWwgcHJpY2UgdmFyaWFibGUgYW5kIHRoZSBuZXcgcHJpY2UgdmFyaWFibGUgY2FsY3VsYXRlZCBhYm92ZSB1c2luZyB0aGUgYG1pbl9yYW5rYCBmdW5jdGlvbi4gQXJlIHRoZXJlIGRpZmZlcmVuY2VzIGluIHRoZSByYW5raW5nIG9mIHRoZSBwcmljZXM/DQoNCiMgVXNpbmcgYHN1bW1hcmlzZWANCmBgYHtyIHN1bW1hcmlzZX0NCmNvbmdyZXNzXzIgPC0gbXV0YXRlKGNvbmdyZXNzX2FnZSwgDQogICAgICAgZGVtb2NyYXQgPSBpZmVsc2UocGFydHkgPT0gJ0QnLCAxLCAwKQ0KICAgICAgICkNCg0Kc3VtbWFyaXNlKGNvbmdyZXNzXzIsIA0KICAgICAgICAgIG51bV9kZW1vY3JhdCA9IHN1bShkZW1vY3JhdCkNCiAgICAgICAgICApDQpgYGANCg0KIyBgZ3JvdXBfYnlgDQpgYGB7ciBncm91cF9ieX0NCmNvbmdyZXNzX2dycCA8LSBncm91cF9ieShjb25ncmVzc18yLCBjb25ncmVzcykNCg0Kc3VtbWFyaXNlKGNvbmdyZXNzX2dycCwgDQogICAgICAgICAgbnVtX2RlbW9jcmF0ID0gc3VtKGRlbW9jcmF0KSwNCiAgICAgICAgICB0b3RhbCA9IG4oKSwNCiAgICAgICAgICBwcm9wX2RlbW9jcmF0ID0gbnVtX2RlbW9jcmF0IC8gdG90YWwNCikNCmBgYA0KDQojIEV4cGxvcmUgdHJlbmQNCmBgYHtyIHRyZW5kLCBldmFsID0gRkFMU0V9DQpudW1fZGVtIDwtIHN1bW1hcmlzZShjb25ncmVzc19ncnAsIA0KICAgICAgICAgICAgICAgICAgICAgbnVtX2RlbW9jcmF0ID0gc3VtKGRlbW9jcmF0KSwNCiAgICAgICAgICAgICAgICAgICAgIHRvdGFsID0gbigpLA0KICAgICAgICAgICAgICAgICAgICAgcHJvcF9kZW1vY3JhdCA9IG51bV9kZW1vY3JhdCAvIHRvdGFsDQopDQpnZ3Bsb3QobnVtX2RlbSwgYWVzKHggPSBjb25ncmVzcywgeSA9IHByb3BfZGVtb2NyYXQpKSArIA0KICBnZW9tX2xpbmUoKQ0KYGBgDQoNCiMgRXhwbG9yZSB0cmVuZCBvdXRwdXQNCmBgYHtyIHRyZW5kMiwgZWNobyA9IEZBTFNFfQ0KbnVtX2RlbSA8LSBzdW1tYXJpc2UoY29uZ3Jlc3NfZ3JwLCANCiAgICAgICAgICAgICAgICAgICAgIG51bV9kZW1vY3JhdCA9IHN1bShkZW1vY3JhdCksDQogICAgICAgICAgICAgICAgICAgICB0b3RhbCA9IG4oKSwNCiAgICAgICAgICAgICAgICAgICAgIHByb3BfZGVtb2NyYXQgPSBudW1fZGVtb2NyYXQgLyB0b3RhbA0KKQ0KZ2dwbG90KG51bV9kZW0sIGFlcyh4ID0gY29uZ3Jlc3MsIHkgPSBwcm9wX2RlbW9jcmF0KSkgKyANCiAgZ2VvbV9saW5lKCkNCmBgYA0KDQojIFlvdXIgVHVybg0KMS4gU3VwcG9zZSB3ZSB3YW50ZWQgdG8gY2FsY3VsYXRlIHRoZSBudW1iZXIgYW5kIHByb3BvcnRpb24gb2YgcmVwdWJsaWNhbnMgaW5zdGVhZCBvZiBkZW1vY3JhdHMsIGFzc3VtaW5nIHRoZXNlIGFyZSB0aGUgb25seSB0d28gcGFydGllcywgZWRpdCB0aGUgYHN1bW1hcmlzZWAgY29tbWFuZCBhYm92ZSB0byBjYWxjdWxhdGUgdGhlc2UgdmFsdWVzLiANCjIuIFN1cHBvc2UgaW5zdGVhZCBvZiB1c2luZyBgc3VtKGRlbW9jcmF0KWAgYWJvdmUsIHdlIHVzZWQgYG1lYW4oZGVtb2NyYXQpYCwgd2hhdCBkb2VzIHRoaXMgdmFsdWUgcmV0dXJuPyBXaHkgZG9lcyBpdCByZXR1cm4gdGhpcyB2YWx1ZT8NCg0KDQojIGBncm91cF9ieWAgd2l0aCBgbXV0YXRlYA0KYGBge3IgbXV0YXRlX2dyb3VwLCBldmFsID0gRkFMU0V9DQpjb25ncmVzc19yZWQgPC0gc2VsZWN0KGNvbmdyZXNzX2FnZSwgY29uZ3Jlc3MsIGNoYW1iZXIsIHN0YXRlLCBwYXJ0eSkNCmNvbmdyZXNzX2dycCA8LSBncm91cF9ieShjb25ncmVzc19yZWQsIGNvbmdyZXNzKQ0KDQptdXRhdGUoY29uZ3Jlc3NfZ3JwLCANCiAgICAgICBkZW1vY3JhdCA9IGlmZWxzZShwYXJ0eSA9PSAnRCcsIDEsIDApLA0KICAgICAgIG51bV9kZW1vY3JhdCA9IHN1bShkZW1vY3JhdCksDQogICAgICAgdG90YWwgPSBuKCksDQogICAgICAgcHJvcF9kZW1vY3JhdCA9IG51bV9kZW1vY3JhdCAvIHRvdGFsDQopDQpgYGANCg0KIyBgZ3JvdXBfYnlgIHdpdGggYG11dGF0ZWAgb3V0cHV0DQpgYGB7ciBtdXRhdGVfZ3JvdXAyLCBlY2hvID0gRkFMU0V9DQpjb25ncmVzc19yZWQgPC0gc2VsZWN0KGNvbmdyZXNzX2FnZSwgY29uZ3Jlc3MsIGNoYW1iZXIsIHN0YXRlLCBwYXJ0eSkNCmNvbmdyZXNzX2dycCA8LSBncm91cF9ieShjb25ncmVzc19yZWQsIGNvbmdyZXNzKQ0KDQptdXRhdGUoY29uZ3Jlc3NfZ3JwLCANCiAgICAgICBkZW1vY3JhdCA9IGlmZWxzZShwYXJ0eSA9PSAnRCcsIDEsIDApLA0KICAgICAgIG51bV9kZW1vY3JhdCA9IHN1bShkZW1vY3JhdCksDQogICAgICAgdG90YWwgPSBuKCksDQogICAgICAgcHJvcF9kZW1vY3JhdCA9IG51bV9kZW1vY3JhdCAvIHRvdGFsDQopDQpgYGANCg0KIyBDaGFpbmluZyBvcGVyYXRpb25zDQpgYGB7ciBjaGFpbl9kaWZmaWN1bHQsIGV2YWwgPSBGQUxTRX0NCnN1bW1hcmlzZSgNCiAgZ3JvdXBfYnkoDQogICAgbXV0YXRlKA0KICAgICAgZmlsdGVyKA0KICAgICAgICBjb25ncmVzc19hZ2UsIGNvbmdyZXNzID49IDEwMA0KICAgICAgKSwgDQogICAgICBkZW1vY3JhdCA9IGlmZWxzZShwYXJ0eSA9PSAnRCcsIDEsIDApDQogICAgKSwNCiAgICBjb25ncmVzcywgY2hhbWJlcg0KICApLA0KICBudW1fZGVtb2NyYXQgPSBzdW0oZGVtb2NyYXQpLA0KICB0b3RhbCA9IG4oKSwNCiAgcHJvcF9kZW1vY3JhdCA9IG51bV9kZW1vY3JhdCAvIHRvdGFsDQopDQpgYGANCg0KIyBUaGUgcGlwZSBgJT4lYCBpcyB0aGUgYW5zd2VyDQpgYGB7ciBwaXBlLCBldmFsID0gRkFMU0V9DQpjb25ncmVzc19hZ2UgJT4lDQogIGZpbHRlcihjb25ncmVzcyA+PSAxMDApICU+JQ0KICBtdXRhdGUoZGVtb2NyYXQgPSBpZmVsc2UocGFydHkgPT0gJ0QnLCAxLCAwKSkgJT4lDQogIGdyb3VwX2J5KGNvbmdyZXNzLCBjaGFtYmVyKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIG51bV9kZW1vY3JhdCA9IHN1bShkZW1vY3JhdCksDQogICAgdG90YWwgPSBuKCksDQogICAgcHJvcF9kZW1vY3JhdCA9IG51bV9kZW1vY3JhdCAvIHRvdGFsDQogICkNCmBgYA0KDQojIFRoZSB0d28gYXJlIGlkZW50aWNhbA0KYGBge3IgaWRlbnRpY2FsfQ0KcGlwZV9jb25ncmVzcyA8LSBjb25ncmVzc19hZ2UgJT4lDQogIGZpbHRlcihjb25ncmVzcyA+PSAxMDApICU+JQ0KICBtdXRhdGUoZGVtb2NyYXQgPSBpZmVsc2UocGFydHkgPT0gJ0QnLCAxLCAwKSkgJT4lDQogIGdyb3VwX2J5KGNvbmdyZXNzLCBjaGFtYmVyKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIG51bV9kZW1vY3JhdCA9IHN1bShkZW1vY3JhdCksDQogICAgdG90YWwgPSBuKCksDQogICAgcHJvcF9kZW1vY3JhdCA9IG51bV9kZW1vY3JhdCAvIHRvdGFsDQogICkNCg0KbmVzdGVkX2NvbmdyZXNzIDwtIHN1bW1hcmlzZSgNCiAgZ3JvdXBfYnkoDQogICAgbXV0YXRlKA0KICAgICAgZmlsdGVyKA0KICAgICAgICBjb25ncmVzc19hZ2UsIGNvbmdyZXNzID49IDEwMA0KICAgICAgKSwgDQogICAgICBkZW1vY3JhdCA9IGlmZWxzZShwYXJ0eSA9PSAnRCcsIDEsIDApDQogICAgKSwNCiAgICBjb25ncmVzcywgY2hhbWJlcg0KICApLA0KICBudW1fZGVtb2NyYXQgPSBzdW0oZGVtb2NyYXQpLA0KICB0b3RhbCA9IG4oKSwNCiAgcHJvcF9kZW1vY3JhdCA9IG51bV9kZW1vY3JhdCAvIHRvdGFsDQopDQoNCmlkZW50aWNhbChwaXBlX2NvbmdyZXNzLCBuZXN0ZWRfY29uZ3Jlc3MpDQpgYGANCg0KDQojIFlvdXIgVHVybg0KMS4gTG9vayBhdCB0aGUgZm9sbG93aW5nIG5lc3RlZCBjb2RlIGFuZCBkZXRlcm1pbmUgd2hhdCBpcyBiZWluZyBkb25lLiBUaGVuIHRyYW5zbGF0ZSB0aGlzIGNvZGUgdG8gdXNlIHRoZSBwaXBlIG9wZXJhdG9yLg0KYGBge3IgY29kZV9waXBlX2V4YW1wLCBldmFsID0gRkFMU0V9DQpzdW1tYXJpc2UoDQogIGdyb3VwX2J5KA0KICAgIG11dGF0ZSgNCiAgICAgIGZpbHRlcigNCiAgICAgICAgZGlhbW9uZHMsIA0KICAgICAgICBjb2xvciAlaW4lIGMoJ0QnLCAnRScsICdGJykgJiBjdXQgJWluJSBjKCdGYWlyJywgJ0dvb2QnLCAnVmVyeSBHb29kJykNCiAgICAgICksDQogICAgICBmX2NvbG9yID0gaWZlbHNlKGNvbG9yID09ICdGJywgMSwgMCksDQogICAgICB2Z19jdXQgPSBpZmVsc2UoY3V0ID09ICdWZXJ5IEdvb2QnLCAxLCAwKQ0KICAgICksDQogICAgY2xhcml0eQ0KICApLA0KICBhdmcgPSBtZWFuKGNhcmF0KSwNCiAgc2QgPSBzZChjYXJhdCksDQogIGF2Z19wID0gbWVhbihwcmljZSksDQogIG51bSA9IG4oKSwNCiAgc3VtbWFyeV9mX2NvbG9yID0gbWVhbihmX2NvbG9yKSwNCiAgc3VtbWFyeV92Z19jdXQgPSBtZWFuKHZnX2N1dCkNCikNCmBgYA0KDQojIFJlYWQgaW4geW91ciBvd24gZGF0YQ0KLSBXZSB3aWxsIHVzZSBkYXRhIHBvc3RlZCB0byBHaXRIdWI6IDxodHRwczovL2dpdGh1Yi5jb20vbGViZWJyMDEvaW93YV9kYXRhX3NjaWVuY2UvdHJlZS9tYXN0ZXIvZGF0YT4NCg0KIyBEYXRhIEltcG9ydA0KYGBge3IgdWZvX3JlYWR9DQp1Zm8gPC0gcmVhZF9jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9sZWJlYnIwMS9pb3dhX2RhdGFfc2NpZW5jZS9tYXN0ZXIvZGF0YS91Zm8uY3N2IikNCmBgYA0KDQojIFNob3cgRGF0YQ0KYGBge3IgdWZvX2RhdGF9DQp1Zm8NCmBgYA0KDQoNCiMgT3RoZXIgdGV4dCBmb3JtYXRzDQotIHRzdiAtIHRhYiBzZXBhcmF0ZWQgZmlsZXMgLSBgcmVhZF90c3ZgDQotIGZpeGVkIHdpZHRoIGZpbGVzIC0gYHJlYWRfZndmYA0KLSB3aGl0ZSBzcGFjZSBnZW5lcmFsbHkgLSBgcmVhZF90YWJsZWANCi0gZGVsaW1pdGVyIGdlbmVyYWxseSAtIGByZWFkX2RlbGltYA0KDQojIFlvdXIgVHVybg0KMS4gVGhlcmUgaXMgYSB0c3YgZmlsZSBwb3N0ZWQgb24gaWNvbiBjYWxsZWQgImxvdHJfY2xlYW4udHN2Ii4gRG93bmxvYWQgdGhpcyBhbmQgcmVhZCB0aGlzIGRhdGEgZmlsZSBpbnRvIFIuIA0KMi4gSW5zdGVhZCBvZiBzcGVjaWZ5aW5nIHRoZSBwYXRoLCB1c2UgdGhlIGZ1bmN0aW9uIGBmaWxlLmNob29zZSgpYC4gRm9yIGV4YW1wbGUsIGByZWFkX3RzdihmaWxlLmNob29zZSgpKWAuIA0KICAgICsgV2hhdCBkb2VzIHRoaXMgZnVuY3Rpb24gZG8/IA0KICAgICsgV291bGQgeW91IHJlY29tbWVuZCB0aGlzIHRvIGJlIHVzZWQgaW4gYSByZXByb2R1Y2libGUgZG9jdW1lbnQ/DQogICAgDQojIEV4Y2VsIEZpbGVzDQpgYGB7ciByZWFkeGwsIGV2YWwgPSBGQUxTRX0NCmluc3RhbGwucGFja2FnZXMoJ3JlYWR4bCcpDQpgYGANCg0KIyBgcmVhZF9leGNlbGANCmBgYHtyIHJlYWRfZXhjZWx9DQpsaWJyYXJ5KHJlYWR4bCkNCnJlYWRfZXhjZWwoJ2RhdGEvdGl0YW5pYy54bHN4JykNCmBgYA0KDQojIEFkZGl0aW9uYWwgUmVzb3VyY2VzDQorIFIgZm9yIERhdGEgU2NpZW5jZTogPGh0dHA6Ly9yNGRzLmhhZC5jby5uei8+DQo=